﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Navigant.Infrastructure.Cases;
using Navigant.Infrastructure.ClientPortals;
using Navigant.Infrastructure.DomainBase;
using Navigant.Infrastructure.Helpers;
using Navigant.Model.ClientPortals;
using Navigant.Model.Custodians;
using Navigant.Model.PreservationNotices;

namespace Navigant.Model.Cases
{
    public class Case : ICase, IAggregateRoot
    {
        #region Constants

        private static class Constants
        {
            public const string RemindersFromAddressKey = "RemindersFromAddress";
        }

        #endregion

        #region Fields

        private Guid id;
        private string name;
        private Uri siteAddress;
        private List<PreservationNoticeTemplate> preservationNoticeTemplates;
        private List<Custodian> custodians;
        private List<HtmlImageData> images;
        private List<CaseSetting> settings;
        private Guid clientPortalId;
        private IClientPortal clientPortal;
        private string remindersFromAddress;
        private Dictionary<string, string> configurationSettings;

        #endregion

        #region Constructors

        internal Case(ICase currentCase)
            : this(currentCase.Id, currentCase.Name, 
                currentCase.SiteAddress, currentCase.ClientPortalId)
        {
            foreach (CaseSetting setting in currentCase.Settings)
            {
                this.settings.Add(setting);
            }
            foreach (string key in currentCase.ConfigurationSettings.Keys)
            {
                this.configurationSettings.Add(key, currentCase.ConfigurationSettings[key]);
            }
        }
        
        public Case(Guid id, string name, Uri siteAddress, Guid clientPortalId)
        {
            this.id = id;
            this.name = name;
            this.SetSiteAddress(siteAddress);
            this.clientPortalId = clientPortalId;
            this.preservationNoticeTemplates = null;
            this.custodians = null;
            this.images = null;
            this.settings = new List<CaseSetting>();
            this.clientPortal = null;
            this.configurationSettings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        }

        #endregion

        #region Properties

        public Guid Id
        {
            get { return this.id; }
        }

        public Guid ClientPortalId
        {
            get { return this.clientPortalId; }
        }

        public IClientPortal ClientPortal
        {
            get 
            {
                if (this.clientPortal == null && this.clientPortalId != Guid.Empty)
                {
                    this.clientPortal = ClientPortalService.GetClientPortalBy(this.clientPortalId);
                }
                return this.clientPortal; 
            }
        }

        public string RemindersFromAddress
        {
            get 
            {
                if (this.remindersFromAddress == null)
                {
                    this.remindersFromAddress = ConfigurationSettingsHelper.GetString(this, 
                        Constants.RemindersFromAddressKey);
                }
                return this.remindersFromAddress;
            }
        }

        public string Name
        {
            get { return this.name; }
        }

        public Uri SiteAddress
        {
            get { return this.siteAddress; }
        }

        public ReadOnlyCollection<HtmlImageData> Images
        {
            get 
            {
                if (this.images == null)
                {
                    this.images = new List<HtmlImageData>(CaseService.GetAllImages(this));
                }
                return this.images.AsReadOnly();
            }
        }

        public ReadOnlyCollection<PreservationNoticeTemplate> PreservationNoticeTemplates
        {
            get { return this.preservationNoticeTemplates.AsReadOnly(); }
        }

        public ReadOnlyCollection<Custodian> Custodians
        {
            get { return this.custodians.AsReadOnly(); }
        }

        public IList<CaseSetting> Settings
        {
            get { return this.settings; }
        }

        public IDictionary<string, string> ConfigurationSettings
        {
            get { return this.configurationSettings; }
        }

        #endregion

        #region GetReminderSetting

        public int GetReminderSetting(string name, bool isInternal, bool isUnacknowledged, int defaultValue)
        {
            int settingValue = defaultValue;
            string settingName = this.GetReminderSettingName(name, isInternal, isUnacknowledged);

            try
            {
                foreach (CaseSetting setting in this.settings)
                {
                    if (setting.Name == settingName)
                    {
                        settingValue = setting.NumberValue;
                        break;
                    }
                }
            }
            catch(Exception e)
            {
                Logger.LogError(e, this);
            }

            return settingValue;
        }

        #endregion

        #region SetReminderSetting

        public void SetReminderSetting(string name, int numberValue, bool isInternal, bool isUnacknowledged)
        {
            string settingName = this.GetReminderSettingName(name, isInternal, isUnacknowledged);
            CaseService.SetSetting(this, settingName, numberValue);
        }

        #endregion

        #region GetReminderSettingName

        private string GetReminderSettingName(string setting, bool isInternal, 
            bool isUnacknowledged)
        {
            return setting + (isInternal ? "Internal" : "External") +
                (isUnacknowledged ? "Unacknowledged" : "Acknowledged");
        }

        #endregion

        #region SetSiteAddress

        private void SetSiteAddress(Uri siteAddress)
        {
            // Account for the fact the Scheme (protocol) of the 
            // Case Uri may be incorrect
            this.siteAddress = siteAddress.ToValidUri();
        }

        #endregion

        #region IEntity Members

        public object Key
        {
            get { return this.id; }
        }

        #endregion

        #region ToString

        public override string ToString()
        {
            return this.Name;
        }

        #endregion

        #region GetHashCode

        public override int GetHashCode()
        {
            return this.id.GetHashCode();
        }

        #endregion

        #region Equality Tests

        /// <summary>
        /// Determines whether the specified entity is equal to the 
        /// current instance.
        /// </summary>
        /// <param name="entity">An <see cref="System.Object"/> that 
        /// will be compared to the current instance.</param>
        /// <returns>True if the passed in entity is equal to the 
        /// current instance.</returns>
        public override bool Equals(object entity)
        {
            return entity != null
                && entity is ICase
                && this == (ICase)entity;
        }

        /// <summary>
        /// Operator overload for determining equality.
        /// </summary>
        /// <param name="case1">The first instance of an 
        /// <see cref="ICase"/>.</param>
        /// <param name="case2">The second instance of an 
        /// <see cref="ICase"/>.</param>
        /// <returns>True if equal.</returns>
        public static bool operator ==(Case case1,
            Case case2)
        {
            // check for both null (cast to object or recursive loop)
            if ((object)case1 == null && (object)case2 == null)
            {
                return true;
            }

            // check for either of them == to null
            if ((object)case1 == null || (object)case2 == null)
            {
                return false;
            }

            if (case1.Id != case2.Id)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Operator overload for determining inequality.
        /// </summary>
        /// <param name="base1">The first instance of an 
        /// <see cref="ICase"/>.</param>
        /// <param name="base2">The second instance of an 
        /// <see cref="ICase"/>.</param>
        /// <returns>True if not equal.</returns>
        public static bool operator !=(Case base1,
            Case base2)
        {
            return (!(base1 == base2));
        }

        #endregion
    }
}
